---
title: 模板表達式參考
i18nReady: true
---

Astro 元件語法是 HTML 的超集合，寫過 HTML 或 JSX 的人會感到熟悉，而且它還支援匯入元件與 JavaScript 表達式。


## 類 JSX 表達式

你可以在 Astro 元件內兩道圍欄（`---`）中的 frontmatter 元件腳本定義本地 JavaScript 變數，接著使用類 JSX 表達式將這些變數注入到元件的 HTML 模板！

:::note[動態（dynamic）vs 互動式（reactive）]
透過這種方式，frontmatter 可以計算並包含**動態**值。一旦包含其中，這些值便不再是**互動式**，之後也無法改變。Astro 元件這種模板只會在算繪階段執行一次而已。

詳見：[Astro 和 JSX 的差異](#astro-和-jsx-的差異)。
:::

### 變數

本地變數可以透過大括弧（curly braces）加入 HTML：

```astro title="src/components/Variables.astro" "{name}"
---
const name = "Astro";
---
<div>
  <h1>Hello {name}!</h1>  <!-- 會輸入成 <h1>Hello Astro!</h1> -->
</div>
```

### 動態屬性

本地變數也能透過大括弧傳遞屬性值給 HTML 元素與元件：

```astro title="src/components/DynamicAttributes.astro" "{name}" "${name}"
---
const name = "Astro";
---
<h1 class={name}>支援屬性表達式</h1>

<MyComponent templateLiteralNameAttribute={`MyNameIs${name}`} />
```

:::caution
HTML 屬性會轉換成字串，因此無法傳遞函式和物件給 HTML 元素。
舉例來說，在 Astro 元件裡，你不能指派 event handler 給 HTML 元素：

```astro title="dont-do-this.astro"
---
function handleClick () {
    console.log("button clicked!");
}
---
<!-- ❌ 無法這麼做！❌ -->
<button onClick={handleClick}>點選這個按鈕，什麼事也不會發生！</button>
```

正確的做法是透過客戶端 JavaScript 建立 event handler，如同原生 JavaScript 寫法：

```astro title="do-this-instead.astro"
---
---
<button id="button">點選這個按鈕</button>
<script>
  function handleClick () {
    console.log("button clicked!");
  }
  document.getElementById("button").addEventListener("click", handleClick);
</script>
```
:::

### 動態 HTML

類 JSX 函式可以使用本地變數動態產生 HTML 元素：

```astro title="src/components/DynamicHtml.astro" "{item}"
---
const items = ["Dog", "Cat", "Platypus"];
---
<ul>
  {items.map((item) => (
    <li>{item}</li>
  ))}
</ul>
```

使用 JSX 邏輯運算子和三元表達式，Astro 能依照條件顯示 HTML。

```astro title="src/components/ConditionalHtml.astro" "visible"
---
const visible = true;
---
{visible && <p>Show me!</p>}

{visible ? <p>Show me!</p> : <p>Else show me!</p>}
```

### 動態標籤

把 HTML 標籤名稱指派給變數，或將匯入的元件再次指派，便可使用動態標籤：

```astro title="src/components/DynamicTags.astro" /Element|(?<!My)Component/
---
import MyComponent from "./MyComponent.astro";
const Element = 'div'
const Component = MyComponent;
---
<Element>Hello!</Element> <!-- 算繪成 <div>Hello!</div> -->
<Component /> <!-- 算繪成 <MyComponent /> -->
```

使用動態標籤時：

- **變數名稱首字要大寫**。舉例來說：使用 `Element` 而不是 `element`。否則，Astro 會嘗試將變數名稱算繪成一般的 HTML 標籤。

- **不支援水合作用指令（hydration directive）**。使用 [`client:*` 水合作用指令](/zh-tw/guides/framework-components/#hydrating-interactive-components)時，Astro 需要事先知道哪個元件會被打包進正式版本，因此這種情況下不適用動態標籤。

- **不支援 [define:vars 指令](/zh-tw/reference/directives-reference/#definevars)**。如果不能為子元素包上額外的元素（例如 `<div>`），你可以手動在元素加上 ``style={`--myVar:${value}`}``。

### Fragment

Astro 支援 `<> </>` 表示法，也有提供內建的 `<Fragment />` 元件。有了它，在使用 [`set:*` 指令](/zh-tw/reference/directives-reference/#sethtml) 時便不需要額外包一層元素。

下面的範例用了 `<Fragment />` 元件算繪出一段文字：

```astro title="src/components/SetHtml.astro" "Fragment"
---
const htmlString = '<p>原始 HTML 內容</p>';
---
<Fragment set:html={htmlString} />
```

### Astro 和 JSX 的差異

Astro 元件語法是 HTML 的超集合，寫過 HTML 或 JSX 的人會感到熟悉。但 `.astro` 檔案和 JSX 仍有些關鍵差異。

#### 屬性

所有屬性在 Astro 都用標準的 `kebab-case` 格式，而 JSX 則使用 `camelCase`。`class` 對 Astro 來說也一樣，然而 React 卻不支援這點。

```jsx del={1} ins={2} title="example.astro"
<div className="box" dataValue="3" />
<div class="box" data-value="3" />
```

#### 複數元素

與 JavaScript 或 JSX 不同，Astro 元件模板能算繪多個元素，不需要額外包一層 `<div>` 或 `<>`。

```astro title="src/components/RootElements.astro"
---
// 有多個元素的模板
---
<p>不需要用容器把多個元素包起來。</p>
<p>Astro 模板支援複數根元素。</p>
```

#### 註解

在 Astro 可以使用標準的 HTML 註解或 JavaScript 風格的註解。

```astro title="example.astro"
---
---
<!-- HTML 註解語法在 .astro 檔案是合法的 -->
{/* JS 註解語法也同樣合法 */}
```

:::caution
HTML 風格的註解會出現在瀏覽器的 DOM，而 JS 風格的註解則不會。如果需要留下待辦註解或其他開發用的訊息，建議使用 JavaScript 風格的註解。
:::


## 元件工具

### `Astro.slots`

`Astro.slots` 有幾個工具函式可以用來修改 Astro 元件由插槽傳入的子元素。

#### `Astro.slots.has()`

<p>

**型別：**`(slotName: string) => boolean`
</p>

你可以用 `Astro.slots.has()` 確認特定插槽名稱的內容是否存在。這在你想包裹插槽內容，但只想在插槽被使用時才算繪包裹元素的時候很好用。

```astro  title="src/pages/index.astro"
---
---
<slot />

{Astro.slots.has('more') && (
  <aside>
    <h2>更多</h2>
    <slot name="more" />
  </aside>
)}
```

#### `Astro.slots.render()`

<p>

**型別：**`(slotName: string, args?: any[]) => Promise<string>`
</p>

你可以用 `Astro.slots.render()` 非同步算繪插槽內容成 HTML 字串。

```astro
---
const html = await Astro.slots.render('default');
---
<Fragment set:html={html} />
```

:::note
這是給進階的使用案例用的！在大多數情況下，用 [`<slot />` 元素](/zh-tw/basics/astro-components/#插槽)算繪插槽內容比較簡單。
:::

`Astro.slots.render()` 可以接受第二個引數：會轉傳到任何子元素函式的參數陣列。這對於自訂工具元件來說很有用。

舉例來說，這個 `<Shout />` 元件將它的 `message` 參數轉換成大寫，傳遞到預設的插槽：

```astro title="src/components/Shout.astro" "await Astro.slots.render('default', [message])"
---
const message = Astro.props.message.toUpperCase();
let html = '';
if (Astro.slots.has('default')) {
  html = await Astro.slots.render('default', [message]);
}
---
<Fragment set:html={html} />
```

做為 `<Shout />` 的子元素傳遞的回呼函式會收到全部大寫的 `message` 參數：

```astro title="src/pages/index.astro"
---
import Shout from "../components/Shout.astro";
---
<Shout message="slots!">
  {(message) => <div>{message}</div>}
</Shout>

<!-- 會算繪成 <div>SLOTS!</div> -->
```

回呼函式可以傳遞到被有 `slot` 屬性的 HTML 元素標籤包著的具名插槽。這個元素只用來將回呼函式傳遞到具名插槽，並不會算繪到頁面上。

```astro
<Shout message="slots!">
  <fragment slot="message">
    {(message) => <div>{message}</div>}
  </fragment>
</Shout>
```

請使用標準的 HTML 元素或不會被解釋為元件的任何小寫標籤（例如 `<fragment>` 而不是 `<Fragment />`）來當做包裹標籤。不要用 HTML `<slot>` 元素，因為會被解釋為 Astro 插槽。

### `Astro.self`

`Astro.self` 讓 Astro 元件能被遞迴呼叫。這個行為讓你用元件模板內的 `<Astro.self>` 從 Astro 元件內算繪它自己。這對於遍歷大量資料儲存和巢狀資料結構有幫助。

```astro
---
// NestedList.astro
const { items } = Astro.props;
---
<ul class="nested-list">
  {items.map((item) => (
    <li>
      <!-- 如果有巢狀資料結構就算繪 `<Astro.self>` -->
      <!-- 還可以由遞迴呼叫傳遞參數過去 -->
      {Array.isArray(item) ? (
        <Astro.self items={item} />
      ) : (
        item
      )}
    </li>
  ))}
</ul>
```

可以像這樣使用這個元件：

```astro
---
import NestedList from './NestedList.astro';
---
<NestedList items={['A', ['B', 'C'], 'D']} />
```

算繪出的 HTML 會像這樣：

```html
<ul class="nested-list">
  <li>A</li>
  <li>
    <ul class="nested-list">
      <li>B</li>
      <li>C</li>
    </ul>
  </li>
  <li>D</li>
</ul>
```